// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <gtest/gtest.h>
#include <climits>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cantor/parser/parser.h>
#include <cantor/utility/json_reader.h>
#include <tests/config.h>
#include <tests/paser/node_check.h>
#include <turbo/flags/flag.h>

TURBO_FLAG(bool, show_json, false, "show json in test");

int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

namespace cantor::parser {
    class DataJson {
    public:
        static DataJson &instance() {
            static DataJson instance;
            return instance;
        }

        const nlohmann::ordered_json &json() const { return json_; }

    private:
        DataJson() {
            std::string json_path = turbo::str_cat(testing::test_root, "/data/select_list.json");
            auto rs = read_file(json_path);
            KCHECK(rs.ok());
            json_ = std::move(rs).value_or_die();
        }

        nlohmann::ordered_json json_;
    };

    struct ScopeTester {
        ScopeTester(const std::string &group, const std::string &case_str, bool show = turbo::get_flag(FLAGS_show_json))
            : group(group), case_str(case_str), show_json(show) {
            auto ins = DataJson::instance().json();
            auto json_group = ins[group].get<nlohmann::json>();
            sql = json_group[case_str].get<std::string>();
            KLOG(INFO) << NOPREFIX << "testing [" << group << ":" << case_str << "] sql<<<" << sql << ">>>";
        }

        ~ScopeTester() {
            KLOG(INFO) << NOPREFIX << "testing done..........";
        }

        std::string group;
        std::string case_str;
        bool show_json;
        std::string sql;
    };

    TEST(test_parser, case_option) {
        //select
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        //select
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            ASSERT_TRUE(select_stmt->fields[0]->org_name.empty());
            ASSERT_TRUE(select_stmt->fields[1]->org_name.empty());
            ASSERT_STREQ(select_stmt->fields[2]->org_name.c_str(), "1+1");
            ASSERT_STREQ(select_stmt->fields[3]->org_name.c_str(), "count(*)");
            ASSERT_STREQ(select_stmt->fields[4]->org_name.c_str(), "(select 1 +1)");
            ASSERT_TRUE(select_stmt->fields[5]->org_name.empty());
            ASSERT_STREQ(select_stmt->fields[6]->org_name.c_str(), "((1.1+1))");
            ASSERT_STREQ(select_stmt->fields[7]->org_name.c_str(), "count(*)");
            ASSERT_STREQ(select_stmt->fields[7]->as_name.c_str(), "A");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt5");
            parser.parse(scoper.sql);
            ASSERT_EQ(1, parser.error);
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt6");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_EXPR);
            auto *func_expr = (cantor::parser::FuncExpr *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("match_against"));
            ASSERT_TRUE(func_expr->exprs.size() == 3);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt7");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_EXPR);
            auto *func_expr = (cantor::parser::FuncExpr *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("match_against"));
            ASSERT_TRUE(func_expr->exprs.size() == 3);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt8");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_EXPR);
            auto *func_expr = (cantor::parser::FuncExpr *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("match_against"));
            ASSERT_TRUE(func_expr->exprs.size() == 3);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt9");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_CASE_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncCaseWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_expr_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_FALSE(select_stmt->is_complex_node());
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt10");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_CASE_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncCaseWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_expr_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            std::cout << select_stmt->to_string() << std::endl;
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt11");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_CASE_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncCaseWhenClause *) (expr);
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_EQ(func_expr->when_clause->else_expr, nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            ASSERT_FALSE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt12");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_NE(func_expr->when_clause->else_expr, nullptr);
            ASSERT_EQ(func_expr->when_clause->case_expr, nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt13");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_EQ(func_expr->when_clause->else_expr, nullptr);
            ASSERT_EQ(func_expr->when_clause->case_expr, nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt14");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_NE(func_expr->when_clause->else_expr, nullptr);
            ASSERT_EQ(func_expr->when_clause->case_expr, nullptr);
            ASSERT_EQ(func_expr->when_clause->when_clauses.size(), 2);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt15");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr != nullptr);
            cantor::parser::DispatchExpr *expr = field->expr;
            ASSERT_TRUE(expr->expr_type == cantor::pb::ast::ENT_FUNC_CASE_WHEN_CLAUSE);
            auto *func_expr = (cantor::parser::FuncCaseWhenClause *) (expr);
            ASSERT_TRUE(std::string(func_expr->fn_name.value) == std::string("case_expr_when"));
            ASSERT_NE(func_expr->when_clause, nullptr);
            ASSERT_NE(func_expr->when_clause->else_expr, nullptr);
            ASSERT_NE(func_expr->when_clause->case_expr, nullptr);
            ASSERT_EQ(func_expr->when_clause->when_clauses.size(), 2);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt16");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_TRUE(select_stmt->select_opt->distinct);
            ASSERT_TRUE(select_stmt->select_opt->sql_cache);
            ASSERT_TRUE(select_stmt->select_opt->calc_found_rows);
            ASSERT_TRUE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(3, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt17");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_FALSE(select_stmt->select_opt->sql_cache);
            ASSERT_FALSE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt18");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_FALSE(select_stmt->select_opt->distinct);
            ASSERT_TRUE(select_stmt->select_opt->sql_cache);
            ASSERT_TRUE(select_stmt->select_opt->calc_found_rows);
            ASSERT_FALSE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(0, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_option", "sql_opt19");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_TRUE(select_stmt->select_opt->distinct);
            ASSERT_TRUE(select_stmt->select_opt->sql_cache);
            ASSERT_TRUE(select_stmt->select_opt->calc_found_rows);
            ASSERT_TRUE(select_stmt->select_opt->straight_join);
            ASSERT_EQ(3, select_stmt->select_opt->priority);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_field) {
        // test select_fields
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr == nullptr);
            ASSERT_TRUE(field->as_name.value == nullptr);
            ASSERT_TRUE(field->wild_card);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr == nullptr);
            ASSERT_TRUE(field->wild_uri.table_name.to_string() == "tablea");
            ASSERT_TRUE(field->wild_uri.db_name.empty());
            ASSERT_TRUE(field->wild_uri.ns_name.empty());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump(4) << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr == nullptr);
            ASSERT_TRUE(field->as_name.value == nullptr);
            ASSERT_TRUE(field->wild_uri.db_name.to_string() == "db");
            ASSERT_TRUE(field->wild_uri.table_name.to_string() == "tablea");
            ASSERT_TRUE(field->wild_card == true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->as_name.value == nullptr);
            ASSERT_TRUE(field->wild_card == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            cantor::parser::SelectStmt *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->as_name.value != nullptr);
            ASSERT_TRUE(std::string(field->as_name.value) == "alias");
            ASSERT_TRUE(field->wild_card == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field5");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            cantor::parser::SelectStmt *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->as_name.value != nullptr);
            ASSERT_TRUE(std::string(field->as_name.value) == "alias");
            ASSERT_TRUE(field->wild_card == false);
            ASSERT_FALSE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field6");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            cantor::parser::SelectStmt *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->as_name.value != nullptr);
            ASSERT_TRUE(std::string(field->as_name.value) == "alias");
            ASSERT_TRUE(field->wild_card == false);
            ASSERT_FALSE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field7");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->as_name.value != nullptr);
            ASSERT_TRUE(std::string(field->as_name.value) == "alias");
            ASSERT_TRUE(field->wild_card == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_field", "sql_field8");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->fields[0]->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(select_stmt->fields[0]->expr == nullptr);
            ASSERT_TRUE(select_stmt->fields[0]->as_name.value == nullptr);
            ASSERT_TRUE(select_stmt->fields[0]->wild_card);
            ASSERT_TRUE(select_stmt->fields[1]->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(select_stmt->fields[1]->expr == nullptr);

            ASSERT_TRUE(select_stmt->fields[1]->wild_uri.table_name.to_string() == "tablea");
            ASSERT_EQ(select_stmt->fields[1]->wild_card, true);
            ASSERT_TRUE(select_stmt->fields[1]->as_name.value == nullptr);
            ASSERT_TRUE(select_stmt->fields[2]->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(select_stmt->fields[2]->expr != nullptr);
            ASSERT_TRUE(select_stmt->fields[2]->as_name.value == nullptr);
            ASSERT_TRUE(select_stmt->fields[2]->wild_card == false);
            ASSERT_TRUE(select_stmt->fields[3]->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(select_stmt->fields[3]->expr != nullptr);
            ASSERT_TRUE(std::string(select_stmt->fields[3]->as_name.value) == "alias_1");
            ASSERT_TRUE(select_stmt->fields[3]->wild_card == false);
            ASSERT_TRUE(select_stmt->fields[4]->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(select_stmt->fields[4]->expr != nullptr);

            ASSERT_TRUE(std::string(select_stmt->fields[4]->as_name.value) == "alias_2");
            ASSERT_TRUE(select_stmt->fields[4]->wild_card == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_orderby) {
        // test select_orderby
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_orderby", "sql_orderby0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        // test select_orderby
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_orderby", "sql_orderby1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(1, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item = order_by->items[0];
            ASSERT_TRUE(by_item->expr != nullptr);
            ASSERT_TRUE(by_item->is_desc == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_orderby", "sql_orderby2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            cantor::parser::SelectStmt *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(1, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item = order_by->items[0];
            ASSERT_TRUE(by_item->expr != nullptr);
            ASSERT_TRUE(by_item->is_desc == false);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_orderby", "sql_orderby3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(1, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item = order_by->items[0];
            ASSERT_TRUE(by_item->expr != nullptr);
            ASSERT_TRUE(by_item->is_desc == true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_orderby", "sql_orderby4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_limit) {
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_limit", "sql_limit0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_limit", "sql_limit1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            cantor::parser::LimitClause *limit = select_stmt->limit;
            std::cout << static_cast<ConstantExpr *>(limit->count)->_u.int64_val << "aaa\n";
            int64_t count = static_cast<ConstantExpr *>(limit->count)->_u.int64_val;
            int64_t offset = static_cast<ConstantExpr *>(limit->offset)->_u.int64_val;
            ASSERT_EQ(10, count);
            ASSERT_EQ(0, offset);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_limit", "sql_limit2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            cantor::parser::LimitClause *limit = select_stmt->limit;
            ASSERT_EQ(100, static_cast<ConstantExpr*>(limit->count)->_u.int64_val);
            ASSERT_EQ(10, static_cast<ConstantExpr*>(limit->offset)->_u.int64_val);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_lock) {
        //test select_lock
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_lock", "sql_lock0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            cantor::parser::LimitClause *limit = select_stmt->limit;
            ASSERT_EQ(100, static_cast<ConstantExpr*>(limit->count)->_u.int64_val);
            ASSERT_EQ(10, static_cast<ConstantExpr*>(limit->offset)->_u.int64_val);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_lock", "sql_lock1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_FOR_UPDATE);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_lock", "sql_lock2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_dual) {
        //test select_from_dual
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_dual", "sql_dual0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_where) {
        // test selec_from where
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_where", "sql_where0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            ASSERT_TRUE(select_stmt->where != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_group) {
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_group", "sql_group0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_TRUE(select_stmt->group != nullptr);
            GroupByClause *group = select_stmt->group;
            ASSERT_EQ(2, group->items.size());
            ASSERT_TRUE(select_stmt->having == nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_group_construct) {
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_group_construct", "sql_group0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_TRUE(select_stmt->group != nullptr);
            GroupByClause *group = select_stmt->group;
            ASSERT_EQ(2, group->items.size());
            ASSERT_TRUE(select_stmt->having == nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_group_construct", "sql_group0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_TRUE(select_stmt->group != nullptr);
            GroupByClause *group = select_stmt->group;
            ASSERT_EQ(2, group->items.size());
            ASSERT_TRUE(select_stmt->having == nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_having) {
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_having", "sql_having0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = select_stmt->order;
            ASSERT_EQ(2, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item_0 = order_by->items[0];
            ASSERT_TRUE(by_item_0->expr != nullptr);
            ASSERT_TRUE(by_item_0->is_desc == false);
            cantor::parser::ByItem *by_item_1 = order_by->items[1];
            ASSERT_TRUE(by_item_1->expr != nullptr);
            ASSERT_TRUE(by_item_1->is_desc == true);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_IN_SHARE);
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_TRUE(select_stmt->group != nullptr);
            GroupByClause *group = select_stmt->group;
            ASSERT_EQ(2, group->items.size());
            ASSERT_TRUE(select_stmt->having != nullptr);
            ASSERT_FALSE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_table_refs) {
        /// 0
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);

            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto *table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(0, table_source->index_hints.size());
            ASSERT_TRUE(table_source->as_name.empty());
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.empty());
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        // test select_from
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            cantor::parser::TableSource *table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(0, table_source->index_hints.size());
            ASSERT_TRUE(table_source->as_name.empty());
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto *table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(0, table_source->index_hints.size());
            ASSERT_TRUE(std::string(table_source->as_name.value) == "table_alias");
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(1, table_source->index_hints.size());
            cantor::parser::IndexHint *index_hint = table_source->index_hints[0];
            ASSERT_EQ(cantor::pb::ast::IHT_HINT_USE, index_hint->hint_type);
            ASSERT_EQ(cantor::pb::ast::IHS_HINT_SCAN, index_hint->hint_scope);
            ASSERT_EQ(0, index_hint->index_name_list.size());
            ASSERT_TRUE(std::string(table_source->as_name.value) == "table_alias");
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(1, table_source->index_hints.size());
            cantor::parser::IndexHint *index_hint = table_source->index_hints[0];
            ASSERT_EQ(cantor::pb::ast::IHT_HINT_USE, index_hint->hint_type);
            ASSERT_EQ(cantor::pb::ast::IHS_HINT_JOIN, index_hint->hint_scope);
            ASSERT_EQ(1, index_hint->index_name_list.size());
            ASSERT_EQ(std::string(index_hint->index_name_list[0].value), "primary_key");
            ASSERT_TRUE(std::string(table_source->as_name.value) == "table_alias");
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from5");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(1, table_source->index_hints.size());
            cantor::parser::IndexHint *index_hint = table_source->index_hints[0];
            ASSERT_EQ(cantor::pb::ast::IHT_HINT_IGNORE, index_hint->hint_type);
            ASSERT_EQ(cantor::pb::ast::IHS_HINT_ORDER_BY, index_hint->hint_scope);
            ASSERT_EQ(2, index_hint->index_name_list.size());
            ASSERT_EQ(std::string(index_hint->index_name_list[0].value), "uniq_key");
            ASSERT_EQ(std::string(index_hint->index_name_list[1].value), "primary");
            ASSERT_TRUE(table_source->as_name.value == nullptr);
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from6");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->source)) == typeid(cantor::parser::TableSource));
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(1, table_source->index_hints.size());
            cantor::parser::IndexHint *index_hint = table_source->index_hints[0];
            ASSERT_EQ(cantor::pb::ast::IHT_HINT_FORCE, index_hint->hint_type);
            ASSERT_EQ(cantor::pb::ast::IHS_HINT_SCAN, index_hint->hint_scope);
            ASSERT_EQ(2, index_hint->index_name_list.size());
            ASSERT_EQ(std::string(index_hint->index_name_list[0].value), "uniq_key");
            ASSERT_EQ(std::string(index_hint->index_name_list[1].value), "primary");
            ASSERT_TRUE(std::string(table_source->as_name.value) == "table_alias");
            ASSERT_TRUE(table_source->table_name != nullptr);
            auto *table_name = table_source->table_name;
            ASSERT_TRUE(table_name->db_name.to_string() == "db");
            ASSERT_TRUE(table_name->table_name.to_string() == "table_a");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from7");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_EQ(nullptr, join_node->expr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from8");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_EQ(nullptr, join_node->expr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from9");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_EQ(nullptr, join_node->expr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from10");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from11");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from12");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.empty());
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from13");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(1, join_node->using_col.size());
            cantor::parser::RangeVar *column_name = join_node->using_col[0];

            ASSERT_TRUE(column_name->column->column_name.to_string() == "id1");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from14");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(2, join_node->using_col.size());
            cantor::parser::RangeVar *column_name0 = join_node->using_col[0];

            ASSERT_TRUE(column_name0->column->column_name.to_string() == "id1");
            cantor::parser::RangeVar *column_name1 = join_node->using_col[1];

            ASSERT_TRUE(column_name1->column->column_name.to_string() == "id2");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from15");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from16");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            cantor::parser::TableSource *left = (cantor::parser::TableSource *) join_node->left->source;
            cantor::parser::TableSource *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from17");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from18");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from19");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(1, join_node->using_col.size());
            cantor::parser::RangeVar *column_name = join_node->using_col[0];

            ASSERT_TRUE(column_name->column->column_name.to_string() == "id1");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from20");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(1, join_node->using_col.size());
            cantor::parser::RangeVar *column_name = join_node->using_col[0];


            ASSERT_TRUE(column_name->column->column_name.to_string() == "id1");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from21");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(1, join_node->using_col.size());
            cantor::parser::RangeVar *column_name = join_node->using_col[0];

            ASSERT_TRUE(column_name->column->column_name.to_string() == "id1");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from22");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(5, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(1, join_node->using_col.size());
            cantor::parser::RangeVar *column_name = join_node->using_col[0];


            ASSERT_TRUE(column_name->column->column_name.to_string() == "id2");
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from23");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_TRUE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from24");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_TRUE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from25");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_TRUE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from26");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_TRUE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left = (cantor::parser::TableSource *) join_node->left->source;
            auto right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from27");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto *join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_TRUE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto *left = (cantor::parser::TableSource *) join_node->left->source;
            auto *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from28");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto *join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_TRUE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto *left = (cantor::parser::TableSource *) join_node->left->source;
            auto *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from29");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_RIGHT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->join_node)) == typeid(cantor::parser::JoinNode));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            cantor::parser::TableSource *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_c");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);

            auto left_join_node = (cantor::parser::JoinNode *) join_node->left->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, left_join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, left_join_node->join_type);
            ASSERT_FALSE(left_join_node->is_natural);
            ASSERT_FALSE(left_join_node->is_straight);
            ASSERT_EQ(0, left_join_node->using_col.size());
            ASSERT_TRUE(left_join_node->expr != nullptr);
            ASSERT_TRUE(left_join_node->left != nullptr);
            ASSERT_TRUE(left_join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(left_join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(left_join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto left_join_node_right = (cantor::parser::TableSource *) left_join_node->right->source;
            ASSERT_TRUE(left_join_node_right->table_name != nullptr);
            ASSERT_TRUE(left_join_node_right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_join_node_right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(left_join_node_right->as_name.value == nullptr);
            ASSERT_TRUE(left_join_node_right->index_hints.size() == 0);
            auto left_join_node_left = (cantor::parser::TableSource *) left_join_node->left->source;
            ASSERT_TRUE(left_join_node_left->table_name != nullptr);
            ASSERT_TRUE(left_join_node_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_join_node_left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left_join_node_left->as_name.value == nullptr);
            ASSERT_TRUE(left_join_node_left->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from30");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr != nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->join_node)) == typeid(cantor::parser::JoinNode));
            auto *left = (cantor::parser::TableSource *) join_node->left->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            auto *right_join_node = (cantor::parser::JoinNode *) join_node->right->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, right_join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, right_join_node->join_type);
            ASSERT_FALSE(right_join_node->is_natural);
            ASSERT_FALSE(right_join_node->is_straight);
            ASSERT_EQ(0, right_join_node->using_col.size());
            ASSERT_TRUE(right_join_node->expr != nullptr);
            ASSERT_TRUE(right_join_node->left != nullptr);
            ASSERT_TRUE(right_join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(right_join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(right_join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto *right_join_node_right = (cantor::parser::TableSource *) right_join_node->right->source;
            ASSERT_TRUE(right_join_node_right->table_name != nullptr);
            ASSERT_TRUE(right_join_node_right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right_join_node_right->table_name->table_name.to_string() == "table_c");
            ASSERT_TRUE(right_join_node_right->as_name.value == nullptr);
            ASSERT_TRUE(right_join_node_right->index_hints.size() == 0);
            auto *right_join_node_left = (cantor::parser::TableSource *) right_join_node->left->source;
            ASSERT_TRUE(right_join_node_left->table_name != nullptr);
            ASSERT_TRUE(right_join_node_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right_join_node_left->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right_join_node_left->as_name.value == nullptr);
            ASSERT_TRUE(right_join_node_left->index_hints.size() == 0);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from31");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto *join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto *left = (cantor::parser::TableSource *) join_node->left->source;
            ASSERT_TRUE(left->table_name != nullptr);
            ASSERT_TRUE(left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left->as_name.value == nullptr);
            ASSERT_TRUE(left->index_hints.size() == 0);

            auto *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        } {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from32");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->is_complex_node());
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->join_node)) == typeid(cantor::parser::JoinNode));
            ASSERT_TRUE(typeid(*(join_node->right->source)) == typeid(cantor::parser::TableSource));
            auto *right = (cantor::parser::TableSource *) join_node->right->source;
            ASSERT_TRUE(right->table_name != nullptr);
            ASSERT_TRUE(right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right->table_name->table_name.to_string() == "table_c");
            ASSERT_TRUE(right->as_name.value == nullptr);
            ASSERT_TRUE(right->index_hints.size() == 0);

            auto *left = (cantor::parser::JoinNode *) join_node->left->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, left->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, left->join_type);
            ASSERT_FALSE(left->is_natural);
            ASSERT_FALSE(left->is_straight);
            ASSERT_EQ(0, left->using_col.size());
            ASSERT_TRUE(left->expr == nullptr);
            ASSERT_TRUE(left->left != nullptr);
            ASSERT_TRUE(left->right != nullptr);
            ASSERT_TRUE(typeid(*(left->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(left->right->source)) == typeid(cantor::parser::TableSource));
            auto *left_right = (cantor::parser::TableSource *) left->right->source;
            ASSERT_TRUE(left_right->table_name != nullptr);
            ASSERT_TRUE(left_right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_right->table_name->table_name.to_string() == "table_b");
            ASSERT_TRUE(left_right->as_name.value == nullptr);
            ASSERT_TRUE(left_right->index_hints.size() == 0);

            auto *left_left = (cantor::parser::TableSource *) left->left->source;
            ASSERT_TRUE(left_left->table_name != nullptr);
            ASSERT_TRUE(left_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left_left->as_name.value == nullptr);
            ASSERT_TRUE(left_left->index_hints.size() == 0);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from33");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_EQ(1, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->order == nullptr);
            ASSERT_TRUE(select_stmt->limit == nullptr);
            ASSERT_TRUE(select_stmt->lock == cantor::pb::ast::SL_NONE);
            ASSERT_TRUE(select_stmt->where == nullptr);
            ASSERT_TRUE(select_stmt->group == nullptr);
            ASSERT_TRUE(select_stmt->having == nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->is_complex_node());
            ASSERT_TRUE(typeid(*(select_stmt->table_refs->join_node)) == typeid(cantor::parser::JoinNode));
            auto *join_node = (cantor::parser::JoinNode *) select_stmt->table_refs->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, join_node->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, join_node->join_type);
            ASSERT_FALSE(join_node->is_natural);
            ASSERT_FALSE(join_node->is_straight);
            ASSERT_EQ(0, join_node->using_col.size());
            ASSERT_TRUE(join_node->expr == nullptr);
            ASSERT_TRUE(join_node->left != nullptr);
            ASSERT_TRUE(join_node->right != nullptr);
            ASSERT_TRUE(typeid(*(join_node->left->join_node)) == typeid(cantor::parser::JoinNode));
            ASSERT_TRUE(typeid(*(join_node->right->join_node)) == typeid(cantor::parser::JoinNode));
            auto *right = (cantor::parser::JoinNode *) join_node->right->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, right->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_INNER_JOIN, right->join_type);
            ASSERT_TRUE(typeid(*(right->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(right->right->source)) == typeid(cantor::parser::TableSource));
            auto *right_left = (cantor::parser::TableSource *) right->left->source;
            auto *right_right = (cantor::parser::TableSource *) right->right->source;
            ASSERT_TRUE(right_left->table_name != nullptr);
            ASSERT_TRUE(right_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right_left->table_name->table_name.to_string() == "table_d");
            ASSERT_TRUE(right_left->as_name.value == nullptr);
            ASSERT_TRUE(right_left->index_hints.size() == 0);

            ASSERT_TRUE(right_right->table_name != nullptr);
            ASSERT_TRUE(right_right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(right_right->table_name->table_name.to_string() == "table_e");
            ASSERT_TRUE(right_right->as_name.value == nullptr);
            ASSERT_TRUE(right_right->index_hints.size() == 0);

            auto *left = (cantor::parser::JoinNode *) join_node->left->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, left->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, left->join_type);
            ASSERT_FALSE(left->is_natural);
            ASSERT_FALSE(left->is_straight);
            ASSERT_EQ(0, left->using_col.size());
            ASSERT_TRUE(left->expr != nullptr);
            ASSERT_TRUE(left->left != nullptr);
            ASSERT_TRUE(left->right != nullptr);
            ASSERT_TRUE(typeid(*(left->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(left->right->join_node)) == typeid(cantor::parser::JoinNode));
            auto *left_left = (cantor::parser::TableSource *) left->left->source;
            ASSERT_TRUE(left_left->table_name != nullptr);
            ASSERT_TRUE(left_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_left->table_name->table_name.to_string() == "table_a");
            ASSERT_TRUE(left_left->as_name.value == nullptr);
            ASSERT_TRUE(left_left->index_hints.size() == 0);

            auto *left_right = (cantor::parser::JoinNode *) left->right->join_node;
            ASSERT_EQ(cantor::pb::ast::ENT_JOIN_NODE, left_right->expr_type);
            ASSERT_EQ(cantor::pb::ast::JT_LEFT_JOIN, left_right->join_type);
            ASSERT_FALSE(left_right->is_natural);
            ASSERT_FALSE(left_right->is_straight);
            ASSERT_EQ(0, left_right->using_col.size());
            ASSERT_TRUE(left_right->expr != nullptr);
            ASSERT_TRUE(left_right->left != nullptr);
            ASSERT_TRUE(left_right->right != nullptr);
            ASSERT_TRUE(typeid(*(left_right->left->source)) == typeid(cantor::parser::TableSource));
            ASSERT_TRUE(typeid(*(left_right->right->source)) == typeid(cantor::parser::TableSource));
            auto *left_right_left = (cantor::parser::TableSource *) left_right->left->source;
            ASSERT_TRUE(left_right_left->table_name != nullptr);
            ASSERT_TRUE(left_right_left->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_right_left->table_name->table_name.to_string() == "table_b");

            auto *left_right_right = (cantor::parser::TableSource *) left_right->right->source;
            ASSERT_TRUE(left_right_right->table_name != nullptr);
            ASSERT_TRUE(left_right_right->table_name->db_name.to_string() == "db");
            ASSERT_TRUE(left_right_right->table_name->table_name.to_string() == "table_c");
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from34");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_EQ(cantor::pb::ast::ENT_FUNC_EXPR, select_stmt->where->expr->expr_type);
            auto where = static_cast<FuncExpr *>(select_stmt->where->expr);
            ASSERT_TRUE(where->exprs.size() == 2);
            turbo::set_flag(&FLAGS_ast_print_sample, false);
            ASSERT_TRUE(std::string("('test')") == where->exprs[1]->to_string()) << where->exprs[1]->to_string();
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from35");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_EQ(cantor::pb::ast::ENT_FUNC_EXPR, select_stmt->where->expr->expr_type);
            auto where = static_cast<FuncExpr *>(select_stmt->where->expr);
            ASSERT_TRUE(where->exprs.size() == 2);
            ASSERT_TRUE(std::string("(1, 2, 3)") == where->exprs[1]->to_string()) << where->exprs[1]->to_string();
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_table_refs", "sql_from36");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            auto *select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_TRUE(select_stmt->where != nullptr);
            ASSERT_EQ(cantor::pb::ast::ENT_FUNC_EXPR, select_stmt->where->expr->expr_type);
            auto where = static_cast<FuncExpr *>(select_stmt->where->expr);
            ASSERT_TRUE(where->exprs.size() == 2);
            ASSERT_TRUE(std::string("('t1', 't2')") == where->exprs[1]->to_string()) << where->exprs[1]->to_string();
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_union) {
        //test union clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_union", "sql_union0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UnionStmt));
            auto *union_stmt = (cantor::parser::UnionStmt *) parser.result[0];
            std::cout << union_stmt->to_string() << std::endl;
            ASSERT_EQ(2, union_stmt->select_stmts.size());
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[0])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[1])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt1 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[0];
            auto *select_stmt2 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[1];
            ASSERT_TRUE(select_stmt1->order != nullptr);
            ASSERT_TRUE(select_stmt1->limit != nullptr);
            ASSERT_TRUE(select_stmt2->order == nullptr);
            ASSERT_TRUE(select_stmt2->limit == nullptr);
            ASSERT_TRUE(union_stmt->limit != nullptr);
            ASSERT_TRUE(union_stmt->order != nullptr);
            if (scoper.show_json) {
                std::cout << union_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_union", "sql_union1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UnionStmt));
            auto *union_stmt = (cantor::parser::UnionStmt *) parser.result[0];
            std::cout << union_stmt->to_string() << std::endl;
            ASSERT_EQ(2, union_stmt->select_stmts.size());
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[0])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[1])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt1 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[0];
            auto *select_stmt2 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[1];
            ASSERT_TRUE(select_stmt1->order != nullptr);
            ASSERT_TRUE(select_stmt1->limit != nullptr);
            ASSERT_TRUE(select_stmt2->order != nullptr);
            ASSERT_TRUE(select_stmt2->limit != nullptr);
            ASSERT_TRUE(union_stmt->limit == nullptr);
            ASSERT_TRUE(union_stmt->order == nullptr);
            if (scoper.show_json) {
                std::cout << union_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_union", "sql_union2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UnionStmt));
            auto *union_stmt = (cantor::parser::UnionStmt *) parser.result[0];
            std::cout << union_stmt->to_string() << std::endl;
            ASSERT_EQ(2, union_stmt->select_stmts.size());
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[0])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[1])) == typeid(cantor::parser::SelectStmt));
            auto *select_stmt1 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[0];
            auto *select_stmt2 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[1];
            ASSERT_TRUE(select_stmt1->order == nullptr);
            ASSERT_TRUE(select_stmt1->limit == nullptr);
            ASSERT_TRUE(select_stmt2->order == nullptr);
            ASSERT_TRUE(select_stmt2->limit == nullptr);
            ASSERT_TRUE(union_stmt->limit != nullptr);
            cantor::parser::LimitClause *limit = union_stmt->limit;
            std::cout << static_cast<ConstantExpr *>(limit->count)->_u.int64_val << "\n";
            int64_t count = static_cast<ConstantExpr *>(limit->count)->_u.int64_val;
            int64_t offset = static_cast<ConstantExpr *>(limit->offset)->_u.int64_val;
            ASSERT_EQ(10, count);
            ASSERT_EQ(0, offset);
            ASSERT_TRUE(union_stmt->order != nullptr);
            cantor::parser::OrderByClause *order_by = union_stmt->order;
            ASSERT_EQ(1, order_by->items.size());
            ASSERT_TRUE(order_by->expr_type == cantor::pb::ast::ENT_ORDER_BY);
            cantor::parser::ByItem *by_item = order_by->items[0];
            ASSERT_TRUE(by_item->expr != nullptr);
            ASSERT_TRUE(by_item->is_desc == false);
            ASSERT_TRUE(union_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << union_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_union", "sql_union3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UnionStmt));
            auto *union_stmt = (cantor::parser::UnionStmt *) parser.result[0];
            std::cout << union_stmt->to_string() << std::endl;
            ASSERT_EQ(3, union_stmt->select_stmts.size());
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[0])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[1])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[2])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt1 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[0];
            auto select_stmt2 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[1];
            auto select_stmt3 = (cantor::parser::SelectStmt *) union_stmt->select_stmts[2];
            ASSERT_TRUE(select_stmt1->order != nullptr);
            ASSERT_TRUE(select_stmt1->limit != nullptr);
            ASSERT_TRUE(select_stmt2->order != nullptr);
            ASSERT_TRUE(select_stmt2->limit != nullptr);
            ASSERT_TRUE(select_stmt3->order == nullptr);
            ASSERT_TRUE(select_stmt3->limit == nullptr);
            ASSERT_TRUE(union_stmt->limit == nullptr);
            ASSERT_TRUE(union_stmt->order != nullptr);
            if (scoper.show_json) {
                std::cout << union_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_union", "sql_union3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::UnionStmt));
            auto union_stmt = (cantor::parser::UnionStmt *) parser.result[0];
            std::cout << union_stmt->to_string() << std::endl;
            ASSERT_EQ(3, union_stmt->select_stmts.size());
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[0])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[1])) == typeid(cantor::parser::SelectStmt));
            ASSERT_TRUE(typeid(*(union_stmt->select_stmts[2])) == typeid(cantor::parser::SelectStmt));
            ASSERT_EQ(true, union_stmt->distinct);
            if (scoper.show_json) {
                std::cout << union_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_from_subselect) {
        //test union clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_from_subselect", "sql_from_subselect0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->table_refs->source != nullptr);
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(std::string(table_source->as_name.value), "t2");
            ASSERT_TRUE(typeid(*(table_source->derived_table)) == typeid(cantor::parser::SelectStmt));
            auto select_stmt1 = (cantor::parser::SelectStmt *) table_source->derived_table;
            ASSERT_TRUE(select_stmt1->fields.size() == 2);
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_select_field_subselect) {
        //test union clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_select_field_subselect", "sql_from_subselect0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->table_refs->source != nullptr);
            auto table_source = (cantor::parser::TableSource *) select_stmt->table_refs->source;
            ASSERT_EQ(std::string(table_source->as_name.value), "t2");
            ASSERT_TRUE(typeid(*(table_source->derived_table)) == typeid(cantor::parser::SelectStmt));
            auto select_stmt1 = (cantor::parser::SelectStmt *) table_source->derived_table;
            ASSERT_TRUE(select_stmt1->fields.size() == 2);
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_exists_subselect) {
        //test exists subquery clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_exists_subselect", "sql_exists_subselect0");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->where->expr->is_exists_subquery_expr());
            auto table_where = (cantor::parser::ExistsSubqueryExpr *) select_stmt->where->expr;
            ASSERT_TRUE(typeid(*(table_where->query_expr)) == typeid(cantor::parser::SubqueryExpr));
            auto query_expr = (cantor::parser::SubqueryExpr *) table_where->query_expr;
            auto select_stmt1 = (cantor::parser::SelectStmt *) (query_expr->query_stmt);
            ASSERT_TRUE(select_stmt1->fields.size() == 2);
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_exists_subselect", "sql_exists_subselect1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            auto exists_expr = (DispatchExpr *) (select_stmt->where->expr);
            ASSERT_TRUE(exists_expr->is_exists_subquery_expr());
            auto sub_expr = (cantor::parser::ExistsSubqueryExpr *) exists_expr;
            ASSERT_TRUE(typeid(*(sub_expr->query_expr)) == typeid(cantor::parser::SubqueryExpr));
            auto select_stmt1 = (cantor::parser::SelectStmt *) (sub_expr->query_expr->query_stmt);
            ASSERT_TRUE(select_stmt1->fields.size() == 2);
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

       TEST(test_parser, case_anyorall_subselect) {
        //test anyorall subquery clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_anyorall_subselect", "sql_anyorall_subselect0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->where->expr)) == typeid(cantor::parser::CompareSubqueryExpr));
            auto sub_expr = (cantor::parser::CompareSubqueryExpr *) select_stmt->where->expr;
            std::cout << sub_expr->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_anyorall_subselect", "sql_anyorall_subselect1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->where->expr)) == typeid(cantor::parser::CompareSubqueryExpr));
            auto sub_expr = (cantor::parser::CompareSubqueryExpr *) select_stmt->where->expr;
            std::cout << sub_expr->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_anyorall_subselect", "sql_anyorall_subselect1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(typeid(*(select_stmt->where->expr)) == typeid(cantor::parser::CompareSubqueryExpr));
            auto sub_expr = (cantor::parser::CompareSubqueryExpr *) select_stmt->where->expr;
            std::cout << sub_expr->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_in_subselect) {
        //test in subquery clause
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_in_subselect", "sql_in_subselect0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            std::cout << static_cast<int>(select_stmt->where->expr->expr_type) << std::endl;
            ASSERT_EQ(cantor::pb::ast::ENT_FUNC_EXPR, select_stmt->where->expr->expr_type);
            auto where = static_cast<FuncExpr *>(select_stmt->where->expr);
            ASSERT_TRUE(where->exprs.size() == 2);
            std::cout << where->exprs[1]->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_in_subselect", "sql_in_subselect1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            std::cout << select_stmt->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_TRUE(select_stmt->table_refs != nullptr);
            ASSERT_EQ(cantor::pb::ast::ENT_FUNC_EXPR, select_stmt->where->expr->expr_type);
            auto where = static_cast<FuncExpr *>(select_stmt->where->expr);
            ASSERT_TRUE(where->exprs.size() == 2);
            std::cout << where->exprs[1]->to_string() << std::endl;
            ASSERT_TRUE(select_stmt->fields.size() == 2);
            ASSERT_TRUE(select_stmt->order != nullptr);
            ASSERT_TRUE(select_stmt->limit != nullptr);
            ASSERT_TRUE(select_stmt->is_complex_node());
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_with_select) {
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte0");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte2");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte3");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_with_select", "sql_with_cte5", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            std::ostringstream with_select_os;
            SelectStmt *select_stmt = (SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_indirect) {
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect0", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            ASSERT_EQ(1, select_stmt->fields.size());
            cantor::parser::SelectField *field = select_stmt->fields[0];
            ASSERT_TRUE(field->expr_type == cantor::pb::ast::ENT_SELECT_FIELD);
            ASSERT_TRUE(field->expr != nullptr);
            ASSERT_TRUE(field->expr->expr_type == cantor::pb::ast::ENT_RANGE_VAR);
            auto rv = static_cast<RangeVar *>(field->expr);
            ASSERT_TRUE(rv->column->table_name.to_string() == "tablea");
            ASSERT_TRUE(rv->column->column_name.to_string() == "col");
            ASSERT_EQ(rv->indirections.size(), 3);
            ASSERT_EQ(rv->indirections[0]->indirection_type, pb::ast::IndirectionType::IND_INDEX);
            ASSERT_EQ(rv->indirections[1]->indirection_type, pb::ast::IndirectionType::IND_INDEX);
            ASSERT_EQ(rv->indirections[2]->indirection_type, pb::ast::IndirectionType::IND_INDEX);
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect1", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect2", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect3", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect4", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect5", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect6", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect7", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect8", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect9", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect10", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect11", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect12", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect13", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect14", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_indirect", "sql_indirect15", true);
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error)<<parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::SelectStmt));
            auto select_stmt = (cantor::parser::SelectStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << select_stmt->to_json().dump() << std::endl;
            }
        }
    }
} // namespace cantor::parser
